/* JUGJobs -- A Jobs Posting Application for Java Users Groups and Other Groups
 * $Id: LegalHtmlScanner.java,v 1.1 2006/02/28 22:04:34 biglee Exp $
 * 
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the JUGJobs project.
 *
 * The Initial Developers of the Original Code are the members of the Triangle
 * Java User's Group in the RTP area of North Carolina.
 * Portions created by the Initial Developer are Copyright (C) 2005
 * the Initial Developers. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** 
 */
package org.jugjobs.util;

import java.util.HashMap;
import java.util.Stack;

/**
 * @author BigLee
 *
 */
public class LegalHtmlScanner extends HTMLScanner {
	
	StringBuffer critique;
	Stack tags;

	public LegalHtmlScanner(String myString) {
		super(myString);
		// TODO Auto-generated constructor stub
	}

	protected void noteProblem(String problem) throws HTMLScanningException {
		critique.append(problem);
		appendContext(sIndex);
		throw new HTMLScanningException(problem);
	}

	private void appendContext(int index) {
		critique.append('\n');
		int backup = index>35?35:index;
		for(int i=backup; i>0; i--) {
			char current = s.charAt(index-i);
			if(Character.isWhitespace(current)) {
				critique.append(' ');
			}
			else if(current=='<') {
				critique.append("&lt;");
			}
			else if(current=='>') {
				critique.append("&gt;");
			}
			else {
				critique.append(current);
			}
		}
		int tailsize = (s.length()>index+10)?10:s.length()-index;
		for(int i=0; i<tailsize; i++) {
			char current = s.charAt(index+i);
			if(Character.isWhitespace(current)) {
				critique.append(' ');
			}
			else if(current=='<') {
				critique.append("&lt;");
			}
			else if(current=='>') {
				critique.append("&gt;");
			}
			else {
				critique.append(current);
			}
		}
		critique.append('\n');
		for(int i=1; i<backup; i++) {
			critique.append('.');
		}
		critique.append("^\n");
	}

	protected void text(String s) throws HTMLScanningException {
		// don't really care about the text.
	}

	protected void wicket() throws HTMLScanningException {
		// nothing to do here.

	}

	protected void closeWicket() throws HTMLScanningException {
		// nothing to do here.

	}

	protected void tag(String tag) throws HTMLScanningException {
		tag = tag.toUpperCase();
		if(!tag.startsWith("/")) {
			// not a closing tag.  Check for legal children of parent tag.
			if(tags.size()>0) {
				String top = (String)tags.peek();
				HashMap legalChildren = (HashMap)(TextAndHtmlUtilities.getLegalChildren().get(top));
				if(legalChildren!=null && legalChildren.get(tag)==null) {
					critique.append("The tag: "+tag+" is not a legal child of "+top);
					appendContext(sIndex);
					throw new HTMLScanningException("Not a legal child.");
				}
			}
			// If it is an opening tag push it.
			if(TextAndHtmlUtilities.getBalancingTagMap().get(tag)!=null) {
				tags.push(tag);
			}
			// else if it is a non-balancing tag ignore it.
			else if(TextAndHtmlUtilities.getNonBalancingTagMap().get(tag)!=null) {
				// nothing to do here.
			}
			// else it is not an allowed tag.  Complain.
			else {
				critique.append("Not an allowed HTML tag: "+tag);
				appendContext(sIndex);
				throw new HTMLScanningException("Not an allowed tag.");
			}
		}
		else {
			// tag is a closing tag.  must match.
			String tail = tag.substring(1);
			
			// ignore close tags for non-balancing tags (eg. </P>)
			if(TextAndHtmlUtilities.getNonBalancingTagMap().get(tail)==null) {
				if(tags.size()>0) {
					String opener = (String)tags.pop();
					if(!tail.equalsIgnoreCase(opener)) {
						critique.append("Unbalanced tag: "+tag+" (Expected \"/"+opener+")");
						appendContext(sIndex);
						throw new HTMLScanningException("Unbalanced tag");
					}
				}
				else {
					critique.append("No matching opening tag for: "+tag);
					appendContext(sIndex);
					throw new HTMLScanningException("Unbalanced tag");
				}
			}
		}

	}

	protected void tagParms(String parms) throws HTMLScanningException {
		// nothing to do here.

	}

	/**
	 * @return critique string or null if no problem found.
	 */
	public String scanForProblems() { //if(true)return null;
		critique = new StringBuffer();
		tags = new Stack();
		try {
			performScan();
		} catch (HTMLScanningException e) {
			return critique.toString();
		}
		if(tags.size()>0) {
			critique.append("Tag not closed: "+(String)tags.pop());
			appendContext(sIndex);
			return critique.toString();
		}
		return null;
	}

	protected boolean proceedOnError() {
		return false;
	}
}
